package com.gitee.cirnochat.chat.service.impl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gitee.cirnochat.chat.filter.MessageFilterChain;
import com.gitee.cirnochat.chat.model.dto.ChatMessage;
import com.gitee.cirnochat.chat.model.enums.ChatSessionType;
import com.gitee.cirnochat.chat.model.enums.MessageOperation;
import com.gitee.cirnochat.chat.model.enums.SignalType;
import com.gitee.cirnochat.chat.model.vo.req.*;
import com.gitee.cirnochat.chat.model.vo.resp.ChatBatchMessageResponse;
import com.gitee.cirnochat.chat.model.vo.resp.ChatMessageResponse;
import com.gitee.cirnochat.chat.model.vo.resp.ChatMessageRevokeResponse;
import com.gitee.cirnochat.chat.model.vo.resp.SignalMessageResponse;
import com.gitee.cirnochat.chat.service.ChatService;
import com.gitee.cirnochat.common.event.SessionStatusChangeEvent;
import com.gitee.cirnochat.common.exception.BusinessException;
import com.gitee.cirnochat.common.exception.MessageBusinessException;
import com.gitee.cirnochat.common.model.enums.ErrorCode;
import com.gitee.cirnochat.user.model.entity.UserPermission;
import com.gitee.cirnochat.user.service.UserService;
import com.gitee.cirnochat.websocket.model.dto.WebSocketUserMessage;
import com.gitee.cirnochat.websocket.model.vo.resp.WebSocketMessageResponse;
import com.gitee.cirnochat.websocket.service.WebSocketService;
import com.gitee.cirnochat.websocket.service.manager.SessionManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.security.Principal;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static com.gitee.cirnochat.common.constant.RedisConstant.CHAT_OFFLINE_MESSAGE_EXPIRE_TIME;
import static com.gitee.cirnochat.common.constant.RedisKey.*;
import static com.gitee.cirnochat.common.constant.WebSocketConstant.CHAT_SUBSCRIBE_CHANNEL;
import static com.gitee.cirnochat.common.constant.WebSocketConstant.WEBRTC_SUBSCRIBE_CHANNEL;

@Service
@Slf4j
@RequiredArgsConstructor
public class ChatServiceImpl implements ChatService {
    private final WebSocketService webSocketService;
    private final StringRedisTemplate redisTemplate;
    private final UserService userService;
    private final ObjectMapper objectMapper;
    private final SessionManager sessionManager;
    private final ApplicationEventPublisher eventPublisher;
    private final RabbitTemplate rabbitTemplate;
    private final MessageFilterChain messageFilterChain;

    private final static int MAX_OFFLINE_MESSAGE_COUNT = 200; // 最大离线消息数量

    @Override
    public void revokeMessage(ChatMessageRevokeRequest revokeRequest, Long userId) {
        Long messageId = revokeRequest.getMessageId();
        Long sessionId = revokeRequest.getSessionId();
        ChatSessionType sessionType = revokeRequest.getSessionType();
        if (sessionType == null || sessionType == ChatSessionType.SINGLE) {
            // 检查离线消息缓存中是否存在该消息，如果存在则撤回（删除）
            final String redisKey = CHAT_OFFLINE_MESSAGE + sessionId;
            redisTemplate.opsForZSet().remove(redisKey, messageId.toString());
            WebSocketUserMessage webSocketUserMessage = new WebSocketUserMessage(
                    sessionId.toString(),
                    CHAT_SUBSCRIBE_CHANNEL,
                    new ChatMessageRevokeResponse(messageId)
            );
            // 同步到在线设备
            webSocketService.sendMessageToOnlineDevicesExcludeCurrent(webSocketUserMessage);
            // 让他人的单聊消息撤回
            webSocketService.sendMessageToOnlineDevices(webSocketUserMessage);
        } else if (sessionType == ChatSessionType.GROUP) {
            // 群聊消息撤回
            WebSocketUserMessage webSocketUserMessage = new WebSocketUserMessage(
                    sessionId.toString(),
                    CHAT_SUBSCRIBE_CHANNEL,
                    new ChatMessageRevokeResponse(messageId)
            );
            // 同步到在线设备
            webSocketService.sendMessageToOnlineDevicesExcludeCurrent(webSocketUserMessage);
            // TODO
//            webSocketService.sendMessageToOnlineDevices(new WebSocketUserMessage(
//                    revokeRequest.getSessionId().toString(),
//                    CHAT_SUBSCRIBE_CHANNEL,
//                    revokeRequest
//            ));
        }
    }

    @Override
    public WebSocketMessageResponse sendChatMessage(ChatMessageSendRequest req, Principal principal) {
        validateMessage(req, principal);
        String receiver = req.getSessionId().toString();
        if (req.getSessionType() == ChatSessionType.SINGLE) {
            req.setSessionId(Long.parseLong(principal.getName()));
        }
        ChatMessageResponse sentMessage = new ChatMessageResponse(req);
        log.info("用户 {} 发送消息给：{}", principal.getName(), receiver);
        sentMessage.setOperation(MessageOperation.RECEIVE);
        if (req.getSessionType() == ChatSessionType.SINGLE) {
            if (sessionManager.isOnline(Long.parseLong(receiver))) {
                WebSocketUserMessage webSocketUserMessage = new WebSocketUserMessage(
                        receiver,
                        CHAT_SUBSCRIBE_CHANNEL,
                        sentMessage
                );
                // 同步到在线设备
                webSocketService.sendMessageToOnlineDevicesExcludeCurrent(webSocketUserMessage);
                // 发送到接收者
                webSocketService.sendMessageToOnlineDevices(webSocketUserMessage);
            } else {
                final String redisKey = CHAT_OFFLINE_MESSAGE + principal.getName();
                try {
                    redisTemplate.opsForZSet().add(redisKey, objectMapper.writeValueAsString(sentMessage), sentMessage.getMessageId());
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
                // 限制最大条数、设置过期
                // 限制最大条数：如果超出最大条数，移除最早的元素
                Long setSize = redisTemplate.opsForZSet().size(redisKey);
                if (setSize != null && setSize > MAX_OFFLINE_MESSAGE_COUNT) {
                    // 获取并移除多余的元素
                    for (int i = 0; i < setSize - MAX_OFFLINE_MESSAGE_COUNT; i++) {
                        redisTemplate.opsForSet().pop(redisKey);
                    }
                }
                redisTemplate.expire(redisKey, CHAT_OFFLINE_MESSAGE_EXPIRE_TIME);
            }
        }
        return WebSocketMessageResponse.Builder
                .messageId(req.getMessageId())
                .message("消息已发送")
                .code(ErrorCode.SUCCESS.getValue())
                .operation(MessageOperation.SENT)
                .build();
    }

    private void validateMessage(ChatMessageSendRequest req, Principal principal) {
        if (req.getUser().getId() == null || req.getUser().getId() <= 0) {
            throw new MessageBusinessException(
                    ErrorCode.PARAMS_ERROR,
                    req.getMessageId(),
                    "用户信息不完整"
            );
        }

        if (!req.getUser().getId().equals(Long.valueOf(principal.getName()))) {
            throw new MessageBusinessException(
                    ErrorCode.PARAMS_ERROR,
                    req.getMessageId(),
                    "用户信息不匹配"
            );
        }

        if (req.getSessionId() == null || req.getSessionId() <= 0) {
            throw new MessageBusinessException(
                    ErrorCode.PARAMS_ERROR,
                    req.getMessageId(),
                    "会话信息不完整"
            );
        }

        if (req.getUser() == null || req.getUser().getId() <= 0) {
            throw new MessageBusinessException(
                    ErrorCode.PARAMS_ERROR,
                    req.getMessageId(),
                    "用户信息不完整"
            );
        }


        if (req.getSessionType().equals(ChatSessionType.SINGLE)) {
            final UserPermission receiver = userService.getUserPermissionById(req.getSessionId(), Long.valueOf(principal.getName()));

            if (receiver == null) {
                throw new MessageBusinessException(
                        ErrorCode.NOT_FOUND_ERROR,
                        req.getMessageId(),
                        "接收者用户不存在"
                );
            }

            // 陌生人判断
            if (receiver.getFriended() == null || !receiver.getFriended()) {
                if (!receiver.getAllowStrangerToSendMessage()) {
                    throw new MessageBusinessException(
                            ErrorCode.OPERATION_ERROR,
                            req.getMessageId(),
                            "对方未添加你为好友，无法发送消息"
                    );
                }
            }

            // 黑名单判断
            if (receiver.getBlockeded()) {
                throw new MessageBusinessException(
                        ErrorCode.OPERATION_ERROR,
                        req.getMessageId(),
                        "对方已将你拉黑，无法发送消息"
                );
            }
        }
    }


    @Override
    public void sendBatchChatMessage(ChatMessageForwardRequest request, Principal principal) {
        Long receiver = request.getSessionId();
        ChatSessionType sessionType = request.getSessionType();
        List<ChatMessage> messages = request.getMessages();

        if (sessionType == ChatSessionType.SINGLE) {
            // 单聊消息转发
            ChatBatchMessageResponse sentMessage = new ChatBatchMessageResponse(
                    MessageOperation.MULTIPLE_RECEIVE ,
                    sessionType, messages, Long.parseLong(principal.getName()));
            if (sessionManager.isOnline(receiver)) {
                WebSocketUserMessage webSocketUserMessage = new WebSocketUserMessage(
                        receiver.toString(),
                        CHAT_SUBSCRIBE_CHANNEL,
                        sentMessage
                );
                // 同步到在线设备
                webSocketService.sendMessageToOnlineDevicesExcludeCurrent(webSocketUserMessage);
                // 发送到接收者
                webSocketService.sendMessageToOnlineDevices(webSocketUserMessage);
            } else {
                final String redisKey = CHAT_OFFLINE_MESSAGE + principal.getName();
                Long lastMessageId = messages.getLast().getMessageId();
                try {
                    // 如果接收者不在线，则将消息存储到离线消息缓存中
                    redisTemplate.opsForZSet().add(redisKey, objectMapper.writeValueAsString(sentMessage), lastMessageId);
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
                // 限制最大条数、设置过期
                // 限制最大条数：如果超出最大条数，移除最早的元素
                Long setSize = redisTemplate.opsForZSet().size(redisKey);
                if (setSize != null && setSize > MAX_OFFLINE_MESSAGE_COUNT) {
                    // 获取并移除多余的元素
                    for (int i = 0; i < setSize - MAX_OFFLINE_MESSAGE_COUNT; i++) {
                        redisTemplate.opsForSet().pop(redisKey);
                    }
                }
                redisTemplate.expire(redisKey, CHAT_OFFLINE_MESSAGE_EXPIRE_TIME);
            }
        } else if (sessionType == ChatSessionType.GROUP) {
            // 群聊消息转发

        } else {
            throw new BusinessException(ErrorCode.OPERATION_ERROR,"转发失败");
        }
    }

    @Override
    public void sendChatSessionStatus(ChatSessionStatusMessageRequest req, Principal principal) {
        eventPublisher.publishEvent(new SessionStatusChangeEvent(this, principal.getName(), req.getSessionId(), req.getTyping()));
    }



    /**
     * 处理信令消息
     *
     * @param message 信令消息
     * @param userId  用户ID
     */
    @Override
    public SignalMessageResponse handleSignalMessage(SignalMessageRequest message, Long userId) {
        SignalType type = message.getType();
        Long receiverId = message.getReceiver();

        // 1. 判断接收者是否存在
        UserPermission receiver = userService.getUserPermissionById(receiverId, userId);
        if (receiver == null) {
            return SignalMessageResponse.builder()
                    .tip("接收者不存在")
                    .type(SignalType.FAILED)
                    .build();
        }

        // 2. 判断接收者是否订阅了webrtc频道
        if (Boolean.FALSE.equals(redisTemplate.hasKey(USER_SUBSCRIBE_CALLING_CHANNEL + receiverId))) {
            return SignalMessageResponse.builder()
                    .tip("接收者未订阅webrtc频道")
                    .type(SignalType.FAILED)
                    .build();
        }

        // 3. 处理不同信令类型
        switch (type) {
            case OFFER -> {
                // 检查对方是否正在通话中
                if (Boolean.TRUE.equals(redisTemplate.hasKey(USER_CALLING_STATUS + receiverId))) {
                    return SignalMessageResponse.builder()
                            .tip("对方正在通话中")
                            .type(SignalType.FAILED)
                            .build();
                }

                // 标记双方通话状态，设置超时（如5分钟）
                redisTemplate.opsForValue().set(USER_CALLING_STATUS + receiverId, "1", 5, TimeUnit.MINUTES);
                redisTemplate.opsForValue().set(USER_CALLING_STATUS + userId, "1", 5, TimeUnit.MINUTES);

                // 推送offer消息到接收者webrtc频道
                webSocketService.sendMessageToOnlineDevices(new WebSocketUserMessage(
                        String.valueOf(receiverId),
                        WEBRTC_SUBSCRIBE_CHANNEL,
                        message
                ));
                return SignalMessageResponse.builder()
                        .tip("通话请求已发送")
                        .type(SignalType.OFFER)
                        .build();
            }

            case ANSWER, CANDIDATE -> {
                // 直接转发answer或candidate给接收者
                webSocketService.sendMessageToOnlineDevices(new WebSocketUserMessage(
                        String.valueOf(receiverId),
                        WEBRTC_SUBSCRIBE_CHANNEL,
                        message
                ));
                return SignalMessageResponse.builder()
                        .tip("信令已转发")
                        .type(type)
                        .build();
            }

            case REJECT -> {
                // 清除双方通话状态
                redisTemplate.delete(USER_CALLING_STATUS + receiverId);
                redisTemplate.delete(USER_CALLING_STATUS + userId);

                // 通知对方挂断
                webSocketService.sendMessageToOnlineDevices(new WebSocketUserMessage(
                        String.valueOf(receiverId),
                        WEBRTC_SUBSCRIBE_CHANNEL,
                        message
                ));
                return SignalMessageResponse.builder()
                        .tip("已挂断通话")
                        .type(SignalType.DISCONNECT)
                        .build();
            }

            default -> {
                return SignalMessageResponse.builder()
                        .tip("未知信令类型")
                        .type(SignalType.FAILED)
                        .build();
            }
        }
    }

}
